﻿using AspNetMVCTodo.Data;
using AspNetMVCTodo.Models;
using AspNetMVCTodo.Services;
using Microsoft.EntityFrameworkCore;

namespace AspNetMvcTodo.UnitTests.Services
{
    public class TodoItemServiceShould
    {
        // 为每个测试方法使用不同的数据库是单元测试中的最佳实践

        [Fact]  // [Fact] 属性是 xUnit.NET 包里带来的，它把这个方法标记为一个测试方法。
        public async Task AddNewItemAsIncompleteWithDueDate()
        {
            // 1. 构建配置选项
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseInMemoryDatabase(databaseName: "Test_AddNewItem").Options;

            // 2. 创建数据库上下文（连接到内存数据库）
            using var context = new ApplicationDbContext(options);

            // 3. 使用上下文进行数据库操作
            var service = new TodoItemService(context);

            var fakeUser = new ApplicationUser
            {
                Id = "fake-000",
                UserName = "fake@example.com"
            };

            await service.AddItemAsync(new TodoItem
            {
                Title = "Testing?"
            }, fakeUser);

            //上面是设置了一个用于写入的 上下文

            // 4. 使用一个新的上下文实例来验证数据
            using var verifyContext = new ApplicationDbContext(options);
            var itemsInDatabase = await verifyContext.Items.CountAsync();
            Assert.Equal(1, itemsInDatabase);

            var item = await verifyContext.Items.FirstAsync();
            Assert.Equal("Testing?", item.Title);
            Assert.False(item.IsDone);

            var difference = DateTimeOffset.Now.AddDays(3) - item?.DueAt;
            Console.WriteLine($"{DateTimeOffset.Now.AddDays(3):yyyy-MM-dd HH:mm:ss}, {item?.DueAt:yyyy-MM-dd HH:mm:ss}, {difference}");
            Assert.True(difference < TimeSpan.FromSeconds(1));


            // Assert 是 xUnit 测试框架中的断言类
        }

        [Fact]
        public async Task MarkDoneAsync_WithNonExistentId_ReturnsFalse()
        {
            // Arrange
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseInMemoryDatabase(databaseName: "Test_MarkDone_NonExistent")
                .Options;

            using var context = new ApplicationDbContext(options);
            var service = new TodoItemService(context);

            var fakeUser = new ApplicationUser
            {
                Id = "user-1",
                UserName = "test@example.com"
            };

            // Act - 使用一个肯定不存在的 GUID
            var nonExistentId = Guid.NewGuid();
            var result = await service.MarkDoneAsync(nonExistentId, fakeUser);

            // Assert
            Assert.False(result);
        }

        [Fact]
        public async Task MarkDoneAsync_WithExistentId_ReturnTrue()
        {
            // Arrange
            var options = new DbContextOptionsBuilder<ApplicationDbContext>().UseInMemoryDatabase(databaseName: "Test_MarkDone_Existent").Options;
            using var context = new ApplicationDbContext(options);
            var service = new TodoItemService(context);

            var fakeUser = new ApplicationUser
            {
                Id = "user-1",
                UserName = "test@example.com"
            };

            // 添加一个测试项目
            await service.AddItemAsync(new TodoItem { Title = "Test Item" }, fakeUser);

            // 获取刚添加的项目的 ID
            var item = await context.Items.FirstAsync();
            var existingId = item.Id;

            // 确保 Item 初始状态是未完成
            Assert.False(item.IsDone);

            // Act
            // 修改 Item 状态为已完成
            var result = await service.MarkDoneAsync(existingId, fakeUser);

            Assert.True(result);

            var updatedItem = await context.Items.FindAsync(existingId);
            Assert.True(updatedItem!.IsDone);
        }

        [Fact]
        public async Task GetIncompleteItemsAsync_ReturnsOnlyIncompleteItems()
        {
            // Arrange
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseInMemoryDatabase(databaseName: "Test_GetIncompleteItems")
                .Options;

            using var context = new ApplicationDbContext(options);
            var service = new TodoItemService(context);

            var fakeUser = new ApplicationUser
            {
                Id = "1765e18d-49f0-4931-928f-c87c761301ec",
                UserName = "ee@gmail.com"
            };

            // 创建两个不同的用户
            var currentUser = new ApplicationUser
            {
                Id = "current-user-id",
                UserName = "current@example.com"
            };

            var otherUser = new ApplicationUser
            {
                Id = "other-user-id",
                UserName = "other@example.com"
            };

            // 为当前用户添加项目
            await service.AddItemAsync(new TodoItem { Title = "Current User Item 1" }, currentUser);
            await service.AddItemAsync(new TodoItem { Title = "Current User Item 2" }, currentUser);

            // 为其他用户添加项目
            await service.AddItemAsync(new TodoItem { Title = "Other User Item 1" }, otherUser);
            await service.AddItemAsync(new TodoItem { Title = "Other User Item 2" }, otherUser);
            await service.AddItemAsync(new TodoItem { Title = "Other User Item 3" }, otherUser);

            // Act
            var result = await service.GetIncompleteItemsAsync(currentUser);

            // Assert
            Assert.NotNull(result);
            Assert.Equal(2, result.Length); // 应该只返回当前用户的 2 个项目

            // 验证所有返回的项目都属于当前用户
            Assert.All(result, item => Assert.Equal(currentUser.Id, item.UserId));

            // 验证包含了正确的项目标题
            Assert.Contains(result, item => item.Title == "Current User Item 1");
            Assert.Contains(result, item => item.Title == "Current User Item 2");

            // 验证不包含其他用户的项目
            Assert.DoesNotContain(result, item => item.Title == "Other User Item 1");
            Assert.DoesNotContain(result, item => item.Title == "Other User Item 2");
            Assert.DoesNotContain(result, item => item.Title == "Other User Item 3");

        }
    }
}
